/*******************************************************************************
 *
 *    Copyright (c) Baina Info Tech Co. Ltd
 *
 *    My_Ringtone
 *    
 *    AudioEditActivity
 *    TODO File description or class description.
 *
 *    @author: zuochen
 *    @since:  2011-1-21
 *    @version: 1.0
 *
 ******************************************************************************/

package com.mp3.dwonloader.ringtonemaker.musicradio.version.zl.activity;

import java.io.File;
import java.io.FileInputStream;
import java.io.RandomAccessFile;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AbsoluteLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.google.ads.AdRequest;
import com.google.ads.AdSize;
import com.google.ads.AdView;
import com.mp3.dwonloader.ringtonemaker.musicradio.version.zl.R;
import com.tools.ringtone.edit.AudioSaveDialog;
import com.tools.ringtone.edit.ContactSelectActivity;
import com.tools.ringtone.edit.MarkerView;
import com.tools.ringtone.edit.SeekTest;
import com.tools.ringtone.edit.SongMetadataReader;
import com.tools.ringtone.edit.TypeSelectDialog;
import com.tools.ringtone.edit.WaveformView;
import com.tools.ringtone.soundfile.CheapSoundFile;
import com.tools.ringtone.util.MusicFileManager;

/**
 * The activity for the Ringdroid main editor window. Keeps track of the
 * waveform display, current horizontal offset, marker handles, start / end text
 * boxes, and handles all of the buttons and controls.
 */
@SuppressWarnings("deprecation")
public class AudioEditActivity extends Activity implements
		MarkerView.MarkerListener, WaveformView.WaveformListener,
		OnClickListener {
	private static final String TAG = AudioEditActivity.class.getSimpleName();
	private long mLoadingStartTime;
	private long mLoadingLastUpdateTime;
	private boolean mLoadingKeepGoing;
	private ProgressDialog mProgressDialog;
	private CheapSoundFile mSoundFile;
	private File mFile;
	private String mFilename;
	private String mDstFilename;
	private String mArtist;
	private String mAlbum;
	private String mGenre;
	private String mTitle;
	private int mYear;
	private String mExtension;
	private String mRecordingFilename;
	private int mNewFileKind;
	private Uri mRecordingUri;
	private boolean mWasGetContentIntent;
	private WaveformView mWaveformView;
	private MarkerView mStartMarker;
	private MarkerView mEndMarker;
	private TextView mStartText;
	private TextView mEndText;
	private TextView mInfo;
	private ImageButton mPlayButton;
	private ImageButton mRewindButton;
	private ImageButton mFfwdButton;
	private ImageButton mZoomInButton;
	private ImageButton mZoomOutButton;
	private ImageButton mSaveButton;
	private boolean mKeyDown;
	private String mCaption = "";
	private int mWidth;
	private int mMaxPos;
	private int mStartPos;
	private int mEndPos;
	private int mLastDisplayedStartPos;
	private int mLastDisplayedEndPos;
	private int mOffset;
	private int mOffsetGoal;
	private int mPlayStartMsec;
	private int mPlayStartOffset;
	private int mPlayEndMsec;
	private Handler mHandler;
	private boolean mIsPlaying;
	private MediaPlayer mPlayer;
	private boolean mCanSeekAccurately;
	private boolean mTouchDragging;
	private float mTouchStart;
	private int mTouchInitialOffset;
	private int mTouchInitialStartPos;
	private int mTouchInitialEndPos;
	private long mWaveformTouchStartMsec;
	private float mDensity;
	private int mMarkerLeftInset;
	private int mMarkerRightInset;
	private int mMarkerTopOffset;
	private int mMarkerBottomOffset;

	// Menu commands
	private static final int CMD_SAVE = 1;
	private static final int CMD_RESET = 2;
	private static final int CMD_ABOUT = 3;

	// Result codes
	private static final int REQUEST_CODE_RECORD = 1;
	private static final int REQUEST_CODE_CHOOSE_CONTACT = 2;

	/**
	 * This is a special intent action that means "edit a sound file".
	 */
	public static final String EDIT = "fastmob.audioedit.action.EDIT";

	/**
	 * Preference names
	 */
	public static final String PREF_SUCCESS_COUNT = "success_count";

	public static final String PREF_STATS_SERVER_CHECK = "stats_server_check";
	public static final String PREF_STATS_SERVER_ALLOWED = "stats_server_allowed";
	public static final String PREF_ERROR_COUNT = "error_count";
	public static final String PREF_ERR_SERVER_CHECK = "err_server_check";
	public static final String PREF_ERR_SERVER_ALLOWED = "err_server_allowed";
	public static final String PREF_UNIQUE_ID = "unique_id";

	/**
	 * Possible codes for PREF_*_SERVER_ALLOWED
	 */
	public static final int SERVER_ALLOWED_UNKNOWN = 0;
	public static final int SERVER_ALLOWED_NO = 1;
	public static final int SERVER_ALLOWED_YES = 2;

	/**
	 * Server url
	 */
	public static final String STATS_SERVER_URL = "http://ringdroid.appspot.com/add";
	public static final String ERR_SERVER_URL = "http://ringdroid.appspot.com/err";

	//
	// Public methods and protected overrides
	//

	/** Called with the activity is first created. */
	@Override
	protected void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		mSaveAll = false;
		mRecordingFilename = null;
		mRecordingUri = null;
		mPlayer = null;
		mIsPlaying = false;

		final Intent intent = getIntent();
		mFilename = intent.getData().toString();

		mWasGetContentIntent = intent.getBooleanExtra("was_get_content_intent",
				false);
		if (mFilename.equals("record")) {
			try {
				final Intent recordIntent = new Intent(
						MediaStore.Audio.Media.RECORD_SOUND_ACTION);
				startActivityForResult(recordIntent, REQUEST_CODE_RECORD);
			} catch (final Exception e) {
				showFinalAlert(e, R.string.record_error);
			}
		}

		mSoundFile = null;
		mKeyDown = false;
		setupViews();
		mHandler = new Handler();
		mHandler.postDelayed(mTimerRunnable, 100);
		if (!mFilename.equals("record")) {
			loadFromFile();
		}
		
		
		final LinearLayout layout = (LinearLayout) findViewById(R.id.linearLayout);
		final AdView adView = new AdView(this, AdSize.BANNER, "a14e3cab66eea7a");
		layout.addView(adView);
		adView.loadAd(new AdRequest());
	}

	/** Called with the activity is finally destroyed. */
	@Override
	protected void onDestroy() {

		if (mPlayer != null && mPlayer.isPlaying()) {
			mPlayer.stop();
		}
		mPlayer = null;

		if (mRecordingFilename != null) {
			try {
				if (!new File(mRecordingFilename).delete()) {
					showFinalAlert(new Exception(), R.string.delete_tmp_error);
				}

				getContentResolver().delete(mRecordingUri, null, null);
			} catch (final SecurityException e) {
				showFinalAlert(e, R.string.delete_tmp_error);
			}
		}

		super.onDestroy();
	}

	/** Called with an Activity we started with an Intent returns. */
	@Override
	protected void onActivityResult(int requestCode, int resultCode,
			Intent dataIntent) {
		if (requestCode == REQUEST_CODE_CHOOSE_CONTACT) {
			// The user finished saving their ringtone and they're
			// just applying it to a contact. When they return here,
			// they're done.
			// sendStatsToServerIfAllowedAndFinish();
			return;
		}

		if (requestCode != REQUEST_CODE_RECORD) {
			return;
		}

		if (resultCode != RESULT_OK) {
			finish();
			return;
		}

		if (dataIntent == null) {
			finish();
			return;
		}

		// Get the recorded file and open it, but save the uri and
		// filename so that we can delete them when we exit; the
		// recorded file is only temporary and only the edited & saved
		// ringtone / other sound will stick around.
		mRecordingUri = dataIntent.getData();
		mRecordingFilename = getFilenameFromUri(mRecordingUri);
		mFilename = mRecordingFilename;
		loadFromFile();
	}

	/**
	 * Called when the orientation changes and/or the keyboard is shown or
	 * hidden. We don't need to recreate the whole activity in this case, but we
	 * do need to redo our layout somewhat.
	 */
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		final int saveZoomLevel = mWaveformView.getZoomLevel();
		super.onConfigurationChanged(newConfig);

		setupViews();
		enableZoomButtons();

		new Handler().postDelayed(new Runnable() {
			public void run() {
				mStartMarker.requestFocus();
				markerFocus(mStartMarker);

				mWaveformView.setZoomLevel(saveZoomLevel);
				mWaveformView.recomputeHeights(mDensity);

				updateDisplay();
			}
		}, 500);
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_SPACE) {
			onPlay(mStartPos);
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

	//
	// WaveformListener
	//

	/**
	 * Every time we get a message that our waveform drew, see if we need to
	 * animate and trigger another redraw.
	 */
	@Override
	public void waveformDraw() {
		mWidth = mWaveformView.getMeasuredWidth();
		if (mOffsetGoal != mOffset && !mKeyDown)
			updateDisplay();
		else if (mIsPlaying) {
			updateDisplay();
		}
	}

	@Override
	public void waveformTouchStart(float x) {
		mTouchDragging = true;
		mTouchStart = x;
		mTouchInitialOffset = mOffset;
		mWaveformTouchStartMsec = System.currentTimeMillis();
	}

	@Override
	public void waveformTouchMove(float x) {
		mOffset = trap((int) (mTouchInitialOffset + (mTouchStart - x)));
		updateDisplay();
	}

	@Override
	public void waveformTouchEnd() {
		mTouchDragging = false;
		mOffsetGoal = mOffset;

		final long elapsedMsec = System.currentTimeMillis() - mWaveformTouchStartMsec;
		if (elapsedMsec < 300) {
			if (mIsPlaying) {
				final int seekMsec = mWaveformView
						.pixelsToMillisecs((int) (mTouchStart + mOffset));
				if (seekMsec >= mPlayStartMsec && seekMsec < mPlayEndMsec) {
					mPlayer.seekTo(seekMsec - mPlayStartOffset);
				} else {
					handlePause();
				}
			} else {
				onPlay((int) (mTouchStart + mOffset));
			}
		}
	}

	//
	// MarkerListener
	//

	@Override
	public void markerDraw() {
	}

	@Override
	public void markerTouchStart(MarkerView marker, float x) {
		mTouchDragging = true;
		mTouchStart = x;
		mTouchInitialStartPos = mStartPos;
		mTouchInitialEndPos = mEndPos;
	}

	@Override
	public void markerTouchMove(MarkerView marker, float x) {
		final float delta = x - mTouchStart;

		if (marker == mStartMarker) {
			mStartPos = trap((int) (mTouchInitialStartPos + delta));
			mEndPos = trap((int) (mTouchInitialEndPos + delta));
		} else {
			mEndPos = trap((int) (mTouchInitialEndPos + delta));
			if (mEndPos < mStartPos)
				mEndPos = mStartPos;
		}

		updateDisplay();
	}

	@Override
	public void markerTouchEnd(MarkerView marker) {
		mTouchDragging = false;
		if (marker == mStartMarker) {
			setOffsetGoalStart();
		} else {
			setOffsetGoalEnd();
		}
	}

	@Override
	public void markerLeft(MarkerView marker, int velocity) {
		mKeyDown = true;

		if (marker == mStartMarker) {
			final int saveStart = mStartPos;
			mStartPos = trap(mStartPos - velocity);
			mEndPos = trap(mEndPos - (saveStart - mStartPos));
			setOffsetGoalStart();
		}

		if (marker == mEndMarker) {
			if (mEndPos == mStartPos) {
				mStartPos = trap(mStartPos - velocity);
				mEndPos = mStartPos;
			} else {
				mEndPos = trap(mEndPos - velocity);
			}

			setOffsetGoalEnd();
		}

		updateDisplay();
	}

	@Override
	public void markerRight(MarkerView marker, int velocity) {
		mKeyDown = true;

		if (marker == mStartMarker) {
			final int saveStart = mStartPos;
			mStartPos += velocity;
			if (mStartPos > mMaxPos)
				mStartPos = mMaxPos;
			mEndPos += (mStartPos - saveStart);
			if (mEndPos > mMaxPos)
				mEndPos = mMaxPos;

			setOffsetGoalStart();
		}

		if (marker == mEndMarker) {
			mEndPos += velocity;
			if (mEndPos > mMaxPos)
				mEndPos = mMaxPos;

			setOffsetGoalEnd();
		}

		updateDisplay();
	}

	@Override
	public void markerEnter(MarkerView marker) {
	}

	@Override
	public void markerKeyUp() {
		mKeyDown = false;
		updateDisplay();
	}

	@Override
	public void markerFocus(MarkerView marker) {
		mKeyDown = false;
		if (marker == mStartMarker) {
			setOffsetGoalStartNoUpdate();
		} else {
			setOffsetGoalEndNoUpdate();
		}

		// Delay updaing the display because if this focus was in
		// response to a touch event, we want to receive the touch
		// event too before updating the display.
		new Handler().postDelayed(new Runnable() {
			@Override
			public void run() {
				updateDisplay();
			}
		}, 100);
	}

	public static void onAbout(final Activity activity) {
		new AlertDialog.Builder(activity).setTitle(R.string.app_name)
				.setMessage(R.string.about_text)
				.setPositiveButton(R.string.alert_ok_button, null)
				.setCancelable(false).show();
	}

	public static void startEdit(final Context aContext, final Uri aFileUri) {
		final Intent intent = new Intent(Intent.ACTION_EDIT, aFileUri, aContext,
				AudioEditActivity.class);
		intent.putExtra("was_get_content_intent", false);
		aContext.startActivity(intent);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.view.View.OnClickListener#onClick(android.view.View)
	 */
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case 0:
			mSaveAll = true;
			onSave();
			break;

		default:
			break;
		}

	}

	@Override
	protected void onPause() {
		if (mPath != null) {
			MusicFileManager.scanFile(AudioEditActivity.this, mPath);
		}
		super.onPause();
	}

	private void setupViews() {
		setContentView(R.layout.audio_edit);

		final DisplayMetrics metrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		mDensity = metrics.density;

		mMarkerLeftInset = (int) (46 * mDensity);
		mMarkerRightInset = (int) (48 * mDensity);
		mMarkerTopOffset = (int) (10 * mDensity);
		mMarkerBottomOffset = (int) (10 * mDensity);

		mStartText = (TextView) findViewById(R.id.starttext);
		mStartText.addTextChangedListener(mTextWatcher);
		mEndText = (TextView) findViewById(R.id.endtext);
		mEndText.addTextChangedListener(mTextWatcher);

		mPlayButton = (ImageButton) findViewById(R.id.play);
		mPlayButton.setOnClickListener(mPlayListener);
		mRewindButton = (ImageButton) findViewById(R.id.rew);
		mRewindButton.setOnClickListener(mRewindListener);
		mFfwdButton = (ImageButton) findViewById(R.id.ffwd);
		mFfwdButton.setOnClickListener(mFfwdListener);
		mZoomInButton = (ImageButton) findViewById(R.id.zoom_in);
		mZoomInButton.setOnClickListener(mZoomInListener);
		mZoomOutButton = (ImageButton) findViewById(R.id.zoom_out);
		mZoomOutButton.setOnClickListener(mZoomOutListener);
		mSaveButton = (ImageButton) findViewById(R.id.save);
		mSaveButton.setOnClickListener(mSaveListener);

		final TextView markStartButton = (TextView) findViewById(R.id.mark_start);
		markStartButton.setOnClickListener(mMarkStartListener);
		final TextView markEndButton = (TextView) findViewById(R.id.mark_end);
		markEndButton.setOnClickListener(mMarkStartListener);

		enableDisableButtons();

		mWaveformView = (WaveformView) findViewById(R.id.waveform);
		mWaveformView.setListener(this);

		mInfo = (TextView) findViewById(R.id.text_info);
		mInfo.setText(mCaption);

		mMaxPos = 0;
		mLastDisplayedStartPos = -1;
		mLastDisplayedEndPos = -1;

		if (mSoundFile != null) {
			mWaveformView.setSoundFile(mSoundFile);
			mWaveformView.recomputeHeights(mDensity);
			mMaxPos = mWaveformView.maxPos();
		}

		mStartMarker = (MarkerView) findViewById(R.id.startmarker);
		mStartMarker.setListener(this);
		mStartMarker.setAlpha(255);
		mStartMarker.setFocusable(true);
		mStartMarker.setFocusableInTouchMode(true);

		mEndMarker = (MarkerView) findViewById(R.id.endmarker);
		mEndMarker.setListener(this);
		mEndMarker.setAlpha(255);
		mEndMarker.setFocusable(true);
		mEndMarker.setFocusableInTouchMode(true);
		// 让第一个按钮获得焦点
		mStartMarker.requestFocus();

		updateDisplay();
		
		
		
	}

	private void loadFromFile() {
		mFile = new File(mFilename);
		mExtension = getExtensionFromFilename(mFilename);

		final SongMetadataReader metadataReader = new SongMetadataReader(this,
				mFilename);
		mTitle = metadataReader.mTitle;
		mArtist = metadataReader.mArtist;
		mAlbum = metadataReader.mAlbum;
		mYear = metadataReader.mYear;
		mGenre = metadataReader.mGenre;

		String titleLabel = mTitle;
		if (mArtist != null && mArtist.length() > 0) {
			titleLabel += " - " + mArtist;
		}
		setTitle(titleLabel);

		mLoadingStartTime = System.currentTimeMillis();
		mLoadingLastUpdateTime = System.currentTimeMillis();
		mLoadingKeepGoing = true;
		mProgressDialog = new ProgressDialog(AudioEditActivity.this);
		mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		mProgressDialog.setTitle(R.string.progress_dialog_loading);
		mProgressDialog.setCancelable(true);
		mProgressDialog
				.setOnCancelListener(new DialogInterface.OnCancelListener() {
					@Override
					public void onCancel(DialogInterface dialog) {
						mLoadingKeepGoing = false;
					}
				});
		mProgressDialog.show();

		final CheapSoundFile.ProgressListener listener = new CheapSoundFile.ProgressListener() {
			@Override
			public boolean reportProgress(double fractionComplete) {
				final long now = System.currentTimeMillis();
				if (now - mLoadingLastUpdateTime > 100) {
					mProgressDialog
							.setProgress((int) (mProgressDialog.getMax() * fractionComplete));
					mLoadingLastUpdateTime = now;
				}
				return mLoadingKeepGoing;
			}
		};

		mCanSeekAccurately = false;
		new Thread() {
			@Override
			public void run() {
				mCanSeekAccurately = SeekTest
						.CanSeekAccurately(getPreferences(Context.MODE_PRIVATE));

				System.out.println("Seek test done, creating media player.");
				try {
					final MediaPlayer player = new MediaPlayer();
					player.setDataSource(mFile.getAbsolutePath());
					player.setAudioStreamType(AudioManager.STREAM_MUSIC);
					player.prepare();
					mPlayer = player;
				} catch (final java.io.IOException e) {
					final Runnable runnable = new Runnable() {
						@Override
						public void run() {
							handleFatalError("ReadError", getResources()
									.getText(R.string.read_error), e);
						}
					};
					mHandler.post(runnable);
				}
				;
			}
		}.start();

		// Load the sound file in a background thread
		new Thread() {
			@Override
			public void run() {
				try {
					mSoundFile = CheapSoundFile.create(mFile.getAbsolutePath(),
							listener);

					if (mSoundFile == null) {
						mProgressDialog.dismiss();
						final String name = mFile.getName().toLowerCase();
						final String[] components = name.split("\\.");
						String err;
						if (components.length < 2) {
							err = getResources().getString(
									R.string.no_extension_error);
						} else {
							err = getResources().getString(
									R.string.bad_extension_error)
									+ " " + components[components.length - 1];
						}
						final String finalErr = err;
						final Runnable runnable = new Runnable() {
							@Override
							public void run() {
								handleFatalError("UnsupportedExtension",
										finalErr, new Exception());
							}
						};
						mHandler.post(runnable);
						return;
					}
				} catch (final Exception e) {
					mProgressDialog.dismiss();
					e.printStackTrace();
					mInfo.setText(e.toString());

					final Runnable runnable = new Runnable() {
						@Override
						public void run() {
							handleFatalError("ReadError", getResources()
									.getText(R.string.read_error), e);
						}
					};
					mHandler.post(runnable);
					return;
				}
				mProgressDialog.dismiss();
				if (mLoadingKeepGoing) {
					final Runnable runnable = new Runnable() {
						@Override
						public void run() {
							finishOpeningSoundFile();
						}
					};
					mHandler.post(runnable);
				} else {
					AudioEditActivity.this.finish();
				}
			}
		}.start();
	}

	private void finishOpeningSoundFile() {
		mWaveformView.setSoundFile(mSoundFile);
		mWaveformView.recomputeHeights(mDensity);

		mMaxPos = mWaveformView.maxPos();
		mLastDisplayedStartPos = -1;
		mLastDisplayedEndPos = -1;

		mTouchDragging = false;

		mOffset = 0;
		mOffsetGoal = 0;
		resetPositions();
		if (mEndPos > mMaxPos)
			mEndPos = mMaxPos;

		mCaption = mSoundFile.getFiletype() + ", " + mSoundFile.getSampleRate()
				+ " Hz, " + mSoundFile.getAvgBitrateKbps() + " kbps, "
				+ formatTime(mMaxPos) + " "
				+ getResources().getString(R.string.time_seconds);
		mInfo.setText(mCaption);

		updateDisplay();
	}

	private synchronized void updateDisplay() {
		if (mIsPlaying) {
			final int now = mPlayer.getCurrentPosition() + mPlayStartOffset;
			final int frames = mWaveformView.millisecsToPixels(now);
			mWaveformView.setPlayback(frames);
			setOffsetGoalNoUpdate(frames - mWidth / 2);
			if (now >= mPlayEndMsec) {
				handlePause();
			}
		}

		if (!mTouchDragging) {
			int offsetDelta = mOffsetGoal - mOffset;

			if (offsetDelta > 10)
				offsetDelta = offsetDelta / 10;
			else if (offsetDelta > 0)
				offsetDelta = 1;
			else if (offsetDelta < -10)
				offsetDelta = offsetDelta / 10;
			else if (offsetDelta < 0)
				offsetDelta = -1;
			else
				offsetDelta = 0;

			mOffset += offsetDelta;
		}

		mWaveformView.setParameters(mStartPos, mEndPos, mOffset);
		mWaveformView.invalidate();

		mStartMarker.setContentDescription(getResources().getText(
				R.string.start_marker)
				+ " " + formatTime(mStartPos));
		mEndMarker.setContentDescription(getResources().getText(
				R.string.end_marker)
				+ " " + formatTime(mEndPos));

		boolean startVisible = true;
		final boolean endVisible = true;

		int startX = mStartPos - mOffset - mMarkerLeftInset;
		if (startX + mStartMarker.getWidth() >= 0) {
			mStartMarker.setAlpha(255);
		} else {
			mStartMarker.setAlpha(0);
			startX = 0;
			startVisible = false;
		}

		int endX = mEndPos - mOffset - mEndMarker.getWidth()
				+ mMarkerRightInset;
		if (endX + mEndMarker.getWidth() >= 0) {
			mEndMarker.setAlpha(255);
		} else {
			mEndMarker.setAlpha(0);
			endX = 0;
			startVisible = false;
		}

		mStartMarker.setLayoutParams(new AbsoluteLayout.LayoutParams(
				AbsoluteLayout.LayoutParams.WRAP_CONTENT,
				AbsoluteLayout.LayoutParams.WRAP_CONTENT, startX,
				mMarkerTopOffset));

		mEndMarker.setLayoutParams(new AbsoluteLayout.LayoutParams(
				AbsoluteLayout.LayoutParams.WRAP_CONTENT,
				AbsoluteLayout.LayoutParams.WRAP_CONTENT, endX, mWaveformView
						.getMeasuredHeight()
						- mEndMarker.getHeight()
						- mMarkerBottomOffset));
	}

	private final Runnable mTimerRunnable = new Runnable() {
		@Override
		public void run() {
			// Updating an EditText is slow on Android. Make sure
			// we only do the update if the text has actually changed.
			if (mStartPos != mLastDisplayedStartPos && !mStartText.hasFocus()) {
				mStartText.setText(formatTime(mStartPos));
				mLastDisplayedStartPos = mStartPos;
			}

			if (mEndPos != mLastDisplayedEndPos && !mEndText.hasFocus()) {
				mEndText.setText(formatTime(mEndPos));
				mLastDisplayedEndPos = mEndPos;
			}

			mHandler.postDelayed(mTimerRunnable, 100);
		}
	};

	private void enableDisableButtons() {
		if (mIsPlaying) {
			mPlayButton.setImageResource(R.drawable.ic_media_pause);
			mPlayButton.setContentDescription(getResources().getText(
					R.string.stop));
		} else {
			mPlayButton.setImageResource(R.drawable.ic_media_play);
			mPlayButton.setContentDescription(getResources().getText(
					R.string.play));
		}
	}

	private void resetPositions() {
		mStartPos = mWaveformView.secondsToPixels(0.0);
		mEndPos = mWaveformView.secondsToPixels(15.0);
	}

	private int trap(int pos) {
		if (pos < 0)
			return 0;
		if (pos > mMaxPos)
			return mMaxPos;
		return pos;
	}

	private void setOffsetGoalStart() {
		setOffsetGoal(mStartPos - mWidth / 2);
	}

	private void setOffsetGoalStartNoUpdate() {
		setOffsetGoalNoUpdate(mStartPos - mWidth / 2);
	}

	private void setOffsetGoalEnd() {
		setOffsetGoal(mEndPos - mWidth / 2);
	}

	private void setOffsetGoalEndNoUpdate() {
		setOffsetGoalNoUpdate(mEndPos - mWidth / 2);
	}

	private void setOffsetGoal(int offset) {
		setOffsetGoalNoUpdate(offset);
		updateDisplay();
	}

	private void setOffsetGoalNoUpdate(int offset) {
		if (mTouchDragging) {
			return;
		}

		mOffsetGoal = offset;
		if (mOffsetGoal + mWidth / 2 > mMaxPos)
			mOffsetGoal = mMaxPos - mWidth / 2;
		if (mOffsetGoal < 0)
			mOffsetGoal = 0;
	}

	private String formatTime(int pixels) {
		if (mWaveformView != null && mWaveformView.isInitialized()) {
			return formatDecimal(mWaveformView.pixelsToSeconds(pixels));
		} else {
			return "";
		}
	}

	private String formatDecimal(double x) {
		int xWhole = (int) x;
		int xFrac = (int) (100 * (x - xWhole) + 0.5);

		if (xFrac >= 100) {
			xWhole++; // Round up
			xFrac -= 100; // Now we need the remainder after the round up
			if (xFrac < 10) {
				xFrac *= 10; // we need a fraction that is 2 digits long
			}
		}

		if (xFrac < 10)
			return xWhole + ".0" + xFrac;
		else
			return xWhole + "." + xFrac;
	}

	private synchronized void handlePause() {
		if (mPlayer != null && mPlayer.isPlaying()) {
			mPlayer.pause();
		}
		mWaveformView.setPlayback(-1);
		mIsPlaying = false;
		enableDisableButtons();
	}

	private synchronized void onPlay(int startPosition) {
		if (mIsPlaying) {
			handlePause();
			return;
		}

		if (mPlayer == null) {
			// Not initialized yet
			return;
		}

		try {
			mPlayStartMsec = mWaveformView.pixelsToMillisecs(startPosition);
			if (startPosition < mStartPos) {
				mPlayEndMsec = mWaveformView.pixelsToMillisecs(mStartPos);
			} else if (startPosition > mEndPos) {
				mPlayEndMsec = mWaveformView.pixelsToMillisecs(mMaxPos);
			} else {
				mPlayEndMsec = mWaveformView.pixelsToMillisecs(mEndPos);
			}

			mPlayStartOffset = 0;

			final int startFrame = mWaveformView
					.secondsToFrames(mPlayStartMsec * 0.001);
			final int endFrame = mWaveformView.secondsToFrames(mPlayEndMsec * 0.001);
			final int startByte = mSoundFile.getSeekableFrameOffset(startFrame);
			final int endByte = mSoundFile.getSeekableFrameOffset(endFrame);
			if (mCanSeekAccurately && startByte >= 0 && endByte >= 0) {
				try {
					mPlayer.reset();
					mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
					final FileInputStream subsetInputStream = new FileInputStream(
							mFile.getAbsolutePath());
					mPlayer.setDataSource(subsetInputStream.getFD(), startByte,
							endByte - startByte);
					mPlayer.prepare();
					mPlayStartOffset = mPlayStartMsec;
				} catch (final Exception e) {
					System.out.println("Exception trying to play file subset");
					mPlayer.reset();
					mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
					mPlayer.setDataSource(mFile.getAbsolutePath());
					mPlayer.prepare();
					mPlayStartOffset = 0;
				}
			}

			mPlayer.setOnCompletionListener(new OnCompletionListener() {
				public synchronized void onCompletion(MediaPlayer arg0) {
					handlePause();
				}
			});
			mIsPlaying = true;

			if (mPlayStartOffset == 0) {
				mPlayer.seekTo(mPlayStartMsec);
			}
			mPlayer.start();
			updateDisplay();
			enableDisableButtons();
		} catch (final Exception e) {
			showFinalAlert(e, R.string.play_error);
			return;
		}
	}

	/**
	 * Show a "final" alert dialog that will exit the activity after the user
	 * clicks on the OK button. If an exception is passed, it's assumed to be an
	 * error condition, and the dialog is presented as an error, and the stack
	 * trace is logged. If there's no exception, it's a success message.
	 */
	private void showFinalAlert(Exception e, CharSequence message) {
		CharSequence title;
		if (e != null) {
			title = getResources().getText(R.string.alert_title_failure);
			setResult(RESULT_CANCELED, new Intent());
		} else {
			title = getResources().getText(R.string.alert_title_success);
		}

		new AlertDialog.Builder(AudioEditActivity.this)
				.setTitle(title)
				.setMessage(message)
				.setPositiveButton(R.string.alert_ok_button,
						new DialogInterface.OnClickListener() {
							@Override
							public void onClick(DialogInterface dialog,
									int whichButton) {
								finish();
							}
						}).setCancelable(false).show();
	}

	private void showFinalAlert(Exception e, int messageResourceId) {
		showFinalAlert(e, getResources().getText(messageResourceId));
	}

	private String makeRingtoneFilename(CharSequence title, String extension) {
		String parentdir;
		switch (mNewFileKind) {
		default:
		case AudioSaveDialog.FILE_KIND_MUSIC:
			parentdir = "/sdcard/media/audio/music";
			break;
		case AudioSaveDialog.FILE_KIND_ALARM:
			parentdir = "/sdcard/media/audio/alarms";
			break;
		case AudioSaveDialog.FILE_KIND_NOTIFICATION:
			parentdir = "/sdcard/media/audio/notifications";
			break;
		case AudioSaveDialog.FILE_KIND_RINGTONE:
			parentdir = "/sdcard/media/audio/ringtones";
			break;
		}

		// Create the parent directory
		final File parentDirFile = new File(parentdir);
		parentDirFile.mkdirs();

		// If we can't write to that special path, try just writing
		// directly to the sdcard
		if (!parentDirFile.isDirectory()) {
			parentdir = "/sdcard";
		}

		// Turn the title into a filename
		String filename = "";
		for (int i = 0; i < title.length(); i++) {
			if (Character.isLetterOrDigit(title.charAt(i))) {
				filename += title.charAt(i);
			}
		}

		// Try to make the filename unique
		String path = null;
		for (int i = 0; i < 100; i++) {
			String testPath;
			if (i > 0)
				testPath = parentdir + "/" + filename + i + extension;
			else
				testPath = parentdir + "/" + filename + extension;

			try {
				final RandomAccessFile f = new RandomAccessFile(new File(testPath),
						"r");
			} catch (final Exception e) {
				// Good, the file didn't exist
				path = testPath;
				break;
			}
		}

		return path;
	}

	private String mPath = null;

	private void saveRingtone(final CharSequence title) {
		final String outPath = makeRingtoneFilename(title, mExtension);

		if (outPath == null) {
			showFinalAlert(new Exception(), R.string.no_unique_filename);
			return;
		}

		mDstFilename = outPath;

		final double startTime = mWaveformView.pixelsToSeconds(mStartPos);
		final double endTime = mWaveformView.pixelsToSeconds(mEndPos);
		final int startFrame = mWaveformView.secondsToFrames(startTime);
		final int endFrame = mWaveformView.secondsToFrames(endTime);
		final int duration = (int) (endTime - startTime + 0.5);

		// Create an indeterminate progress dialog
		mProgressDialog = new ProgressDialog(this);
		mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		mProgressDialog.setTitle(R.string.progress_dialog_saving);
		mProgressDialog.setIndeterminate(true);
		mProgressDialog.setCancelable(false);
		mProgressDialog.show();

		// Save the sound file in a background thread
		new Thread() {
			@Override
			public void run() {
				final File outFile = new File(outPath);
				try {
					// Write the new file
					if (mSaveAll) {
						mSoundFile.WriteFile(outFile, 0,
								mSoundFile.getNumFrames());
					} else {
						mSoundFile.WriteFile(outFile, startFrame, endFrame
								- startFrame);
					}

					// Try to load the new file to make sure it worked
					final CheapSoundFile.ProgressListener listener = new CheapSoundFile.ProgressListener() {
						public boolean reportProgress(double frac) {
							// Do nothing - we're not going to try to
							// estimate when reloading a saved sound
							// since it's usually fast, but hard to
							// estimate anyway.
							return true; // Keep going
						}
					};
					CheapSoundFile.create(outPath, listener);
					mPath = outPath;
				} catch (final Exception e) {
					mProgressDialog.dismiss();

					// CharSequence errorMessage;
					// if (e.getMessage().equals("No space left on device")) {
					// errorMessage = getResources().getText(
					// R.string.no_space_error);
					// e = null;
					// } else {
					// errorMessage = getResources().getText(
					// R.string.write_error);
					// }
					//
					// final CharSequence finalErrorMessage = errorMessage;
					// final Exception finalException = e;
					// Runnable runnable = new Runnable() {
					// public void run() {
					// handleFatalError(
					// "WriteError",
					// finalErrorMessage,
					// finalException);
					// }
					// };
					// mHandler.post(runnable);
					return;
				}

				mProgressDialog.dismiss();

				final Runnable runnable = new Runnable() {
					public void run() {
						afterSavingRingtone(title, outPath, outFile, duration);
					}
				};
				mHandler.post(runnable);
			}
		}.start();
	}

	private void afterSavingRingtone(CharSequence title, String outPath,
			File outFile, int duration) {
		final long length = outFile.length();
		if (length <= 512) {
			outFile.delete();
			new AlertDialog.Builder(this)
					.setTitle(R.string.alert_title_failure)
					.setMessage(R.string.too_small_error)
					.setPositiveButton(R.string.alert_ok_button, null)
					.setCancelable(false).show();
			return;
		}

		// Create the database record, pointing to the existing file path

		final long fileSize = outFile.length();
		final String mimeType = "audio/mpeg";

		final String artist = "" + getResources().getText(R.string.artist_name);

		final ContentValues values = new ContentValues();
		values.put(MediaStore.MediaColumns.DATA, outPath);
		values.put(MediaStore.MediaColumns.TITLE, title.toString());
		values.put(MediaStore.MediaColumns.SIZE, fileSize);
		values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);

		values.put(MediaStore.Audio.Media.ARTIST, artist);
		values.put(MediaStore.Audio.Media.DURATION, duration);

		values.put(MediaStore.Audio.Media.IS_RINGTONE,
				mNewFileKind == AudioSaveDialog.FILE_KIND_RINGTONE);
		values.put(MediaStore.Audio.Media.IS_NOTIFICATION,
				mNewFileKind == AudioSaveDialog.FILE_KIND_NOTIFICATION);
		values.put(MediaStore.Audio.Media.IS_ALARM,
				mNewFileKind == AudioSaveDialog.FILE_KIND_ALARM);
		values.put(MediaStore.Audio.Media.IS_MUSIC,
				mNewFileKind == AudioSaveDialog.FILE_KIND_MUSIC);

		// Insert it into the database
		final Uri uri = MediaStore.Audio.Media.getContentUriForPath(outPath);
		final Uri newUri = getContentResolver().insert(uri, values);
		setResult(RESULT_OK, new Intent().setData(newUri));

		// Update a preference that counts how many times we've
		// successfully saved a ringtone or other audio
		final SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
		final int successCount = prefs.getInt(PREF_SUCCESS_COUNT, 0);
		final SharedPreferences.Editor prefsEditor = prefs.edit();
		prefsEditor.putInt(PREF_SUCCESS_COUNT, successCount + 1);
		prefsEditor.commit();

		// If Ringdroid was launched to get content, just return
		if (mWasGetContentIntent) {
			// sendStatsToServerIfAllowedAndFinish();
			return;
		}

		// There's nothing more to do with music or an alarm. Show a
		// success message and then quit.
		if (mNewFileKind == AudioSaveDialog.FILE_KIND_MUSIC
				|| mNewFileKind == AudioSaveDialog.FILE_KIND_ALARM) {
			Toast.makeText(this, R.string.save_success_message,
					Toast.LENGTH_SHORT).show();
			// sendStatsToServerIfAllowedAndFinish();
			return;
		}

		// If it's a notification, give the user the option of making
		// this their default notification. If they say no, we're finished.
		if (mNewFileKind == AudioSaveDialog.FILE_KIND_NOTIFICATION) {
			new AlertDialog.Builder(AudioEditActivity.this)
					.setTitle(R.string.alert_title_success)
					.setMessage(R.string.set_default_notification)
					.setPositiveButton(R.string.alert_yes_button,
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int whichButton) {
									RingtoneManager
											.setActualDefaultRingtoneUri(
													AudioEditActivity.this,
													RingtoneManager.TYPE_NOTIFICATION,
													newUri);
									// sendStatsToServerIfAllowedAndFinish();
								}
							})
					.setNegativeButton(R.string.alert_no_button,
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog,
										int whichButton) {
									// sendStatsToServerIfAllowedAndFinish();
								}
							}).setCancelable(false).show();
			return;
		}

		// If we get here, that means the type is a ringtone. There are
		// three choices: make this your default ringtone, assign it to a
		// contact, or do nothing.

		final Handler handler = new Handler() {
			@Override
			public void handleMessage(Message response) {
				final int actionId = response.arg1;
				switch (actionId) {
				case R.id.button_make_default:
					RingtoneManager.setActualDefaultRingtoneUri(
							AudioEditActivity.this,
							RingtoneManager.TYPE_RINGTONE, newUri);
					Toast.makeText(AudioEditActivity.this,
							R.string.default_ringtone_success_message,
							Toast.LENGTH_SHORT).show();
					// //sendStatsToServerIfAllowedAndFinish();
					break;
				case R.id.button_choose_contact:
					chooseContactForRingtone(newUri);
					break;
				default:
				case R.id.button_do_nothing:
					// //sendStatsToServerIfAllowedAndFinish();
					break;
				}
			}
		};
		final Message message = Message.obtain(handler);
		final TypeSelectDialog dlog = new TypeSelectDialog(this, message);
		dlog.show();
	}

	private void chooseContactForRingtone(Uri uri) {
		final Intent intent = new Intent(Intent.ACTION_EDIT, uri);
		intent.setClass(this, ContactSelectActivity.class);
		startActivityForResult(intent, REQUEST_CODE_CHOOSE_CONTACT);
	}

	private void handleFatalError(final CharSequence errorInternalName,
			final CharSequence errorString, final Exception exception) {

		final SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
		final int failureCount = prefs.getInt(PREF_ERROR_COUNT, 0);
		final SharedPreferences.Editor prefsEditor = prefs.edit();
		prefsEditor.putInt(PREF_ERROR_COUNT, failureCount + 1);
		prefsEditor.commit();

		// Check if we already have a pref for whether or not we can
		// contact the server.
		final int serverAllowed = prefs.getInt(PREF_ERR_SERVER_ALLOWED,
				SERVER_ALLOWED_UNKNOWN);

		if (serverAllowed == SERVER_ALLOWED_NO) {

			// Just show a simple "write error" message
			showFinalAlert(exception, errorString);
			return;
		}

		if (serverAllowed == SERVER_ALLOWED_YES) {

			new AlertDialog.Builder(AudioEditActivity.this)
					.setTitle(R.string.app_name)
					.setMessage(errorString)
					.setPositiveButton(R.string.alert_ok_button,
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int whichButton) {
									// //sendErrToServerAndFinish(errorInternalName,exception);
									return;
								}
							}).setCancelable(false).show();
			return;
		}

		// The number of times the user must have had a failure before
		// we'll ask them. Defaults to 1, and each time they click "Later"
		// we double and add 1.
		final int allowServerCheckIndex = prefs
				.getInt(PREF_ERR_SERVER_CHECK, 1);
		if (failureCount < allowServerCheckIndex) {
			// Just show a simple "write error" message
			showFinalAlert(exception, errorString);
			return;
		}

		new AlertDialog.Builder(this)
				.setTitle(R.string.app_name)
				.setMessage(
						errorString
								+ ". "
								+ getResources().getText(
										R.string.error_server_prompt))
				.setPositiveButton(R.string.server_yes,
						new DialogInterface.OnClickListener() {
							@Override
							public void onClick(DialogInterface dialog,
									int whichButton) {
								prefsEditor.putInt(PREF_ERR_SERVER_ALLOWED,
										SERVER_ALLOWED_YES);
								prefsEditor.commit();
								// //sendErrToServerAndFinish(errorInternalName,
								// exception);
							}
						})
				.setNeutralButton(R.string.server_later,
						new DialogInterface.OnClickListener() {
							@Override
							public void onClick(DialogInterface dialog,
									int whichButton) {
								prefsEditor.putInt(PREF_ERR_SERVER_CHECK,
										1 + allowServerCheckIndex * 2);
								prefsEditor.commit();
								finish();
							}
						})
				.setNegativeButton(R.string.server_never,
						new DialogInterface.OnClickListener() {
							@Override
							public void onClick(DialogInterface dialog,
									int whichButton) {
								prefsEditor.putInt(PREF_ERR_SERVER_ALLOWED,
										SERVER_ALLOWED_NO);
								prefsEditor.commit();
								finish();
							}
						}).setCancelable(false).show();
	}

	private void onSave() {

		if (mIsPlaying) {
			handlePause();
		}

		final Handler handler = new Handler() {
			@Override
			public void handleMessage(Message response) {
				final CharSequence newTitle = (CharSequence) response.obj;
				mNewFileKind = response.arg1;
				saveRingtone(newTitle);
			}
		};

		final Message message = Message.obtain(handler);
		final AudioSaveDialog dlog = new AudioSaveDialog(this, getResources(),
				mTitle, message);
		dlog.show();
	}

	private void enableZoomButtons() {
		mZoomInButton.setEnabled(mWaveformView.canZoomIn());
		mZoomOutButton.setEnabled(mWaveformView.canZoomOut());
	}

	private final OnClickListener mSaveListener = new OnClickListener() {
		@Override
		public void onClick(View sender) {
			onSave();
		}
	};

	private final OnClickListener mPlayListener = new OnClickListener() {
		@Override
		public void onClick(View sender) {
			onPlay(mStartPos);
		}
	};

	private final OnClickListener mZoomInListener = new OnClickListener() {
		@Override
		public void onClick(View sender) {
			mWaveformView.zoomIn();
			mStartPos = mWaveformView.getStart();
			mEndPos = mWaveformView.getEnd();
			mMaxPos = mWaveformView.maxPos();
			mOffset = mWaveformView.getOffset();
			mOffsetGoal = mOffset;
			enableZoomButtons();
			updateDisplay();
		}
	};

	private final OnClickListener mZoomOutListener = new OnClickListener() {
		@Override
		public void onClick(View sender) {
			mWaveformView.zoomOut();
			mStartPos = mWaveformView.getStart();
			mEndPos = mWaveformView.getEnd();
			mMaxPos = mWaveformView.maxPos();
			mOffset = mWaveformView.getOffset();
			mOffsetGoal = mOffset;
			enableZoomButtons();
			updateDisplay();
		}
	};

	private final OnClickListener mRewindListener = new OnClickListener() {
		@Override
		public void onClick(View sender) {
			if (mIsPlaying) {
				int newPos = mPlayer.getCurrentPosition() - 5000;
				if (newPos < mPlayStartMsec)
					newPos = mPlayStartMsec;
				mPlayer.seekTo(newPos);
			} else {
				mStartMarker.requestFocus();
				markerFocus(mStartMarker);
			}
		}
	};

	private final OnClickListener mFfwdListener = new OnClickListener() {
		@Override
		public void onClick(View sender) {
			if (mIsPlaying) {
				int newPos = 5000 + mPlayer.getCurrentPosition();
				if (newPos > mPlayEndMsec)
					newPos = mPlayEndMsec;
				mPlayer.seekTo(newPos);
			} else {
				mEndMarker.requestFocus();
				markerFocus(mEndMarker);
			}
		}
	};

	private final OnClickListener mMarkStartListener = new OnClickListener() {
		@Override
		public void onClick(View sender) {
			if (mIsPlaying) {
				mStartPos = mWaveformView.millisecsToPixels(mPlayer
						.getCurrentPosition() + mPlayStartOffset);
				updateDisplay();
			}
		}
	};

	private final OnClickListener mMarkEndListener = new OnClickListener() {
		@Override
		public void onClick(View sender) {
			if (mIsPlaying) {
				mEndPos = mWaveformView.millisecsToPixels(mPlayer
						.getCurrentPosition() + mPlayStartOffset);
				updateDisplay();
				handlePause();
			}
		}
	};

	private final TextWatcher mTextWatcher = new TextWatcher() {
		@Override
		public void beforeTextChanged(CharSequence s, int start, int count,
				int after) {
		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before,
				int count) {
		}

		@Override
		public void afterTextChanged(Editable s) {

			double max = 0.0;
			if (mStartText.hasFocus()) {
				try {
					max = mWaveformView.pixelsToSeconds(mMaxPos);
					double start = Double.parseDouble(mStartText.getText()
							.toString());
					if (start < 0 || start > max) {
						start = 0;
						mStartText.setText("0");
						Toast.makeText(AudioEditActivity.this,
								"Illegal number.", Toast.LENGTH_SHORT).show();
					}
					mStartPos = mWaveformView.secondsToPixels(start);
					updateDisplay();
				} catch (final NumberFormatException e) {
				}
			}
			if (mEndText.hasFocus()) {
				try {
					max = mWaveformView.pixelsToSeconds(mMaxPos);
					double end = Double.parseDouble(mEndText.getText()
							.toString());
					if (end < 0 || end > max) {
						end = max;
						mEndText.setText(max + "");
						Toast.makeText(AudioEditActivity.this,
								"Illegal number.", Toast.LENGTH_SHORT).show();
					}
					mEndPos = mWaveformView.secondsToPixels(end);
					updateDisplay();
				} catch (final NumberFormatException e) {
				}
			}
		}
	};
	private boolean mSaveAll = false;

	/**
	 * Return extension including dot, like ".mp3"
	 */
	private String getExtensionFromFilename(String filename) {
		return filename.substring(filename.lastIndexOf('.'), filename.length());
	}

	private String getFilenameFromUri(Uri uri) {
		final Cursor c = managedQuery(uri, null, "", null, null);
		if (c.getCount() == 0) {
			return null;
		}
		c.moveToFirst();
		final int dataIndex = c.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);

		return c.getString(dataIndex);
	}

}
